/**
 * Project Name:Micro-Services-Common
 * File Name   :AnnotationScanHandler.java
 * Package Name:com.micro.services.common.annotation
 * Date:2015-9-6下午2:53:05
 * Copyright (c) 2015, http://my.oschina.net/httpssl All Rights Reserved.
 *
*/
/**
 *  {{Code Template Comments Files}}
 */

package com.micro.services.common.annotation;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * ClassName:AnnotationScanHandler <br/>
 * Function :扫描带注解的类. <br/>
 * Reason	:TODO ADD REASON. <br/>
 * Date     :2015-9-6 下午2:53:05 <br/>
 * @author  :http://my.oschina.net/httpssl
 * @email   :491835898@QQ.COM
 * @since   :JDK 1.7
 * @see 	 
 */
public final class AnnotationScanHandler
{
	private static final Logger log = LoggerFactory.getLogger(AnnotationScanHandler.class);

	private static final String RESOURCE_PATTERN = "/**/*.class";

	private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	private final List<String> packagesList = new LinkedList<String>();

	private final List<TypeFilter> typeFilters = new LinkedList<TypeFilter>();

	private static final ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersBeanNameMap = new ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>>();

	/**
	 * 构造函数
	 * 
	 * @param packagesToScan
	 *            指定哪些包需要被扫描,支持多个包"package.a,package.b"并对每个包都会递归搜索
	 * @param annotationFilter
	 *            指定扫描包中含有特定注解标记的bean,支持多个注解
	 */
	@SafeVarargs
	public AnnotationScanHandler(String[] packagesToScan, Class<? extends Annotation>... annotationFilter) {
		if (packagesToScan != null) {
			for (String packagePath : packagesToScan) {
				this.packagesList.add(packagePath);
			}
		}
		if (annotationFilter != null) {
			for (Class<? extends Annotation> annotation : annotationFilter) {
				typeFilters.add(new AnnotationTypeFilter(annotation, false));
			}
		}
	}

	/**
	 * 将符合条件的Bean以Class集合的形式返回
	 * 
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public void init()
	{
		log.info("业务Handler注解工厂类，扫描系统中所有的业务处理类");
		beforeHandlerResource(handlersBeanNameMap);
	}

	private void beforeHandlerResource(ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersMap)
	{
		try {

			if (!this.packagesList.isEmpty()) {
				for (String pkg : this.packagesList) {
					String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
							+ ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
					Resource[] resources = this.resourcePatternResolver.getResources(pattern);
					MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);

					onHandlerResource(resources, readerFactory, handlersMap);

				}
			}
		} catch (IOException e) {
			log.error("扫描业务处理异常", e);
		}
	}

	private void onHandlerResource(Resource[] resources, MetadataReaderFactory readerFactory,
			ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersMap) throws IOException
	{

		if (resources == null || readerFactory == null) {
			return;
		}

		for (Resource resource : resources) {
			if (resource.isReadable()) {
				MetadataReader reader = readerFactory.getMetadataReader(resource);
				// 这儿可以抽象出来，但是因为本项目只需要一个注解扫描
				if (matchesEntityTypeFilter(reader, readerFactory)) {
					Map<String, Object> map = reader.getAnnotationMetadata().getAnnotationAttributes(
							"com.wms.studio.annotations.Handler");
					if (map != null) {
						String handlerName = (String) map.get("handlerName");
						String beanName = (String) map.get("beanName");
						HandlerScope scope = (HandlerScope) map.get("scope");
						if (scope == HandlerScope.None) {
							continue;
						}

						addHandler(handlerName, beanName, scope, handlersMap);
					}
				}
			}
		}
	}

	private void addHandler(String handlerName, String beanName, HandlerScope scope,
			ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersMap)
	{

		if (StringUtils.isEmpty(beanName)|| StringUtils.isEmpty(handlerName)) {
			return;
		}

		ConcurrentHashMap<HandlerScope, LinkedList<String>> handlerNameHandler = handlersMap.get(handlerName);

		if (handlerNameHandler == null) {
			handlerNameHandler = new ConcurrentHashMap<HandlerScope, LinkedList<String>>();
			handlersMap.put(handlerName, handlerNameHandler);
		}

		LinkedList<String> handlerApiBeans = handlerNameHandler.get(scope);

		if (handlerApiBeans == null) {
			handlerApiBeans = new LinkedList<String>();
			handlerNameHandler.put(scope, handlerApiBeans);
		}

		handlerApiBeans.add(beanName);
	}

	/**
	 * 检查当前扫描到的Bean含有任何一个指定的注解标记
	 * 
	 * @param reader
	 * @param readerFactory
	 * @return
	 * @throws IOException
	 */
	private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException
	{
		if (!this.typeFilters.isEmpty()) {
			for (TypeFilter filter : this.typeFilters) {
				if (filter.match(reader, readerFactory)) {
					return true;
				}
			}
		}
		return false;
	}

	public List<String> getBeforeHandlers(String handlerName)
	{

		return getHandlers(handlerName, HandlerScope.Before);
	}

	public List<String> getAfterHandlers(String handlerName)
	{
		return getHandlers(handlerName, HandlerScope.After);
	}

	public List<String> getHandlers(String handlerName, HandlerScope scope)
	{

		if (StringUtils.isEmpty(handlerName) || scope == null || HandlerScope.None.equals(scope)) {
			return Collections.emptyList();
		}
		ConcurrentHashMap<HandlerScope, LinkedList<String>> handlerNameHandler = handlersBeanNameMap.get(handlerName);
		if (handlerNameHandler == null) {
			return Collections.emptyList();
		}

		LinkedList<String> handlerApis = handlerNameHandler.get(scope);

		if (handlerApis == null) {
			return Collections.emptyList();
		}

		return Collections.unmodifiableList(handlerApis);
	}

	public synchronized void refresh()
	{
		final ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> tempHandlersMap = new ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>>();
		beforeHandlerResource(tempHandlersMap);
		handlersBeanNameMap.clear();
		handlersBeanNameMap.putAll(tempHandlersMap);
	}
}
